
#include "RtBase.h"
#include "RtTransportBase.h"
#include "RtSocket.h"
#include "RtTimeValue.h"

#ifdef RT_WIN32
  #include <ws2tcpip.h>
#endif // RT_WIN32

#if defined (RT_SUPPORT_QOS) && defined (RT_WIN32)
  #include <Qos.h>
#endif // RT_SUPPORT_QOS && RT_WIN32

CRtString RtGetSystemErrorInfo(int inErrno);

CRtTransportBase::CRtTransportBase(IRtReactor *pReactor)
	: m_pSink(NULL)
	, m_pReactor(pReactor)
{
	RT_ASSERTE(m_pReactor);
/***********Jobs Add at 2008.06.03 for flow control************************/
	m_dwTotalSend = 0;
	m_dwTotalRecv = 0;
	m_dwLastSendCalTime = m_dwLastRecvCalTime = GetTickCountEx();
	m_dwLastRecvRate = 0;
	m_dwLastSendRate = 0;
/***********Jobs Add at 2008.06.03 end************************/
}

CRtTransportBase::~CRtTransportBase()
{
}

DWORD CRtTransportBase::AddReference()
{
	return CRtReferenceControlSingleThreadTimerDelete::AddReference();
}

DWORD CRtTransportBase::ReleaseReference()
{
	return CRtReferenceControlSingleThreadTimerDelete::ReleaseReference();
}

RtResult CRtTransportBase::OpenWithSink(IRtTransportSink *aSink)
{
	RT_ASSERTE_RETURN(aSink, RT_ERROR_INVALID_ARG);
//	RT_INFO_TRACE_THIS("CRtTransportBase::OpenWithSink pSink = " << aSink);

	// we allow the upper layer invokes this function many times.
	if (m_pSink) {
		m_pSink = aSink;
		return RT_OK;
	}
	else {
		m_pSink = aSink;
	}

	RtResult rv = Open_t();
	if (RT_FAILED(rv)) {
		Close_t(RT_OK);
		m_pSink = NULL;
	}
	return rv;
}

IRtTransportSink* CRtTransportBase::GetSink()
{
	return m_pSink;
}

RtResult CRtTransportBase::GetOption(DWORD aCommand, LPVOID aArg)
{
	switch (aCommand) {
/***********Jobs Add at 2008.06.03 for flow control************************/
	case CS_OPT_RECV_RATE:
		{
			LONGLONG dwCurrent = GetTickCountEx();
			DWORD  dwRet = 0;
			DWORD dwRate;
			if(dwCurrent >= m_dwLastRecvCalTime + 1000)//long than 1 seconds
			{
				dwRate = m_dwTotalRecv* 1000/(DWORD)(dwCurrent - m_dwLastRecvCalTime);
				m_dwTotalRecv = 0;
				m_dwLastRecvCalTime = dwCurrent;

				if(m_dwLastRecvRate)
					dwRet = (9 * dwRate + m_dwLastRecvRate)/10;
				else
					dwRet = dwRate;

				m_dwLastRecvRate = dwRate;
			}
			*(static_cast<DWORD *>(aArg)) = dwRet;
			return RT_OK;
		}
	case CS_OPT_SEND_RATE:
		{
			LONGLONG dwCurrent = GetTickCountEx();
			DWORD  dwRet = 0;
			DWORD dwRate;
			if(dwCurrent >= m_dwLastSendCalTime + 1000)//long than 1 seconds
			{
				dwRate = m_dwTotalSend* 1000/(DWORD)(dwCurrent - m_dwLastSendCalTime);
				m_dwTotalSend = 0;
				m_dwLastSendCalTime = dwCurrent;

				if(m_dwLastSendRate)
					dwRet = (9 * dwRate + m_dwLastSendRate)/10;
				else
					dwRet = dwRate;
				m_dwLastSendRate = dwRate;
			}
			*(static_cast<DWORD *>(aArg)) = dwRet;
			return RT_OK;
		}
		/***********Jobs Add at 2008.06.03 end************************/

	default:
		RT_WARNING_TRACE_THIS("CRtTransportBase::GetOption,"
			" unknow aCommand=" << aCommand << 
			" aArg=" << aArg);
		return RT_ERROR_INVALID_ARG;
	}
}

RtResult CRtTransportBase::Disconnect(RtResult aReason)
{
//	RT_INFO_TRACE_THIS("CRtTransportBase::Disconnect areason = " << aReason << " pSink = " << m_pSink);
	RtResult rv = Close_t(aReason);
	m_pSink = NULL;
	return rv;
}

int CRtTransportBase::OnClose(RT_HANDLE aFd, MASK aMask)
{
	RT_INFO_TRACE_THIS("CRtTransportBase::OnClose, fd="<<aFd<<" sink="<<m_pSink);

	Close_t(RT_OK);
	IRtTransportSink *pTmp = m_pSink;
	m_pSink = NULL;

	RT_ASSERTE(pTmp);
	if (pTmp)
		pTmp->OnDisconnect(RT_ERROR_NETWORK_SOCKET_ERROR, this);
	return 0;
}

RtResult CRtTransportBase::SetTos2Socket(CRtSocketBase &aSocket, LPVOID aArg)
{
#ifdef RT_WIN32
	static BOOL s_bSetWin32Registry = FALSE;
	if (!s_bSetWin32Registry) {
		s_bSetWin32Registry = TRUE;
		OSVERSIONINFO osvi;
		ZeroMemory(&osvi, sizeof(osvi));
		osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
		if (!::GetVersionEx(&osvi)) {
			RT_ERROR_TRACE_THIS("CRtTransportBase::SetTos2Socket,"
				" GetVersionEx() failed! err=" << ::GetLastError());
		}

		if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion >= 5) 
		{
			HKEY hKey;
			LONG lRet = ::RegOpenKeyEx(
				HKEY_LOCAL_MACHINE, 
				"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", 
				0, 
				KEY_SET_VALUE, 
				&hKey);
			if (lRet == ERROR_SUCCESS) {
				DWORD dwValue = 0;
				lRet = ::RegSetValueEx(
					hKey, 
					"DisableUserTOSSetting", 
					0, 
					REG_DWORD, 
					(LPBYTE)&dwValue, 
					sizeof(DWORD));
				if (lRet != ERROR_SUCCESS) {
					RT_ERROR_TRACE_THIS("CRtTransportBase::SetTos2Socket,"
						" RegSetValueEx() failed! lRet=" << lRet);
				}
			}
			else {
				RT_ERROR_TRACE_THIS("CRtTransportBase::SetTos2Socket,"
					" RegOpenKeyEx() failed! lRet=" << lRet);
			}
		}
	}
#endif // RT_WIN32

	int nTos = 1;
	int nLen = sizeof(int);
	if (aSocket.GetOption(IPPROTO_IP, IP_TOS, &nTos, &nLen) == -1) {
		RT_ERROR_TRACE_THIS("CRtTransportBase::SetTos2Socket, GetOption(IP_TOS) failed! err=" << RtGetSystemErrorInfo(errno));
	}
	if (aSocket.SetOption(IPPROTO_IP, IP_TOS, aArg, sizeof(int)) == -1) {
		RT_ERROR_TRACE_THIS("CRtTransportBase::SetTos2Socket, SetOption(IP_TOS) failed! err=" << RtGetSystemErrorInfo(errno));
		return RT_ERROR_NETWORK_SOCKET_ERROR;
	}
	else
		return RT_OK;
}

#ifdef RT_SUPPORT_QOS
RtResult CRtTransportBase::SetQos2Socket(RT_HANDLE aSocket)
{
	RT_ASSERTE(aSocket != RT_INVALID_HANDLE);

#ifdef RT_WIN32
	QOS Qos;
	::memset(&Qos, QOS_NOT_SPECIFIED, sizeof(QOS));
	
	Qos.ReceivingFlowspec.ServiceType           = SERVICETYPE_NOTRAFFIC;
    Qos.ReceivingFlowspec.TokenRate             = QOS_NOT_SPECIFIED; 
    Qos.ReceivingFlowspec.TokenBucketSize       = QOS_NOT_SPECIFIED; 
    Qos.ReceivingFlowspec.PeakBandwidth         = QOS_NOT_SPECIFIED; 
	Qos.ReceivingFlowspec.Latency			    = QOS_NOT_SPECIFIED;
	Qos.ReceivingFlowspec.DelayVariation        = QOS_NOT_SPECIFIED; 
    Qos.ReceivingFlowspec.MinimumPolicedSize    = QOS_NOT_SPECIFIED; 
    Qos.ReceivingFlowspec.MaxSduSize            = QOS_NOT_SPECIFIED; 
    
	Qos.ProviderSpecific.len = 0;
    Qos.ProviderSpecific.buf = NULL;
	
#ifndef SERVICETYPE_QUALITATIVE
  #define SERVICETYPE_QUALITATIVE 0x0000000D
#endif // SERVICETYPE_QUALITATIVE

    Qos.SendingFlowspec.ServiceType             = SERVICETYPE_QUALITATIVE; 
    Qos.SendingFlowspec.TokenRate               = QOS_NOT_SPECIFIED; 
    Qos.SendingFlowspec.TokenBucketSize         = QOS_NOT_SPECIFIED; 
    Qos.SendingFlowspec.PeakBandwidth           = QOS_NOT_SPECIFIED; 
	Qos.SendingFlowspec.Latency			        = QOS_NOT_SPECIFIED;
	Qos.SendingFlowspec.DelayVariation          = QOS_NOT_SPECIFIED; 
    Qos.SendingFlowspec.MinimumPolicedSize      = QOS_NOT_SPECIFIED; 
    Qos.SendingFlowspec.MaxSduSize              = QOS_NOT_SPECIFIED;
/*
	//g711
	Qos.SendingFlowspec.ServiceType             = SERVICETYPE_GUARANTEED; 
    Qos.SendingFlowspec.TokenRate               = 9250; 
    Qos.SendingFlowspec.TokenBucketSize         = 680; 
    Qos.SendingFlowspec.PeakBandwidth           = 13875; 
	Qos.SendingFlowspec.Latency			        = QOS_NOT_SPECIFIED;
	Qos.SendingFlowspec.DelayVariation          = QOS_NOT_SPECIFIED; 
    Qos.SendingFlowspec.MinimumPolicedSize      = 340; 
    Qos.SendingFlowspec.MaxSduSize              = 340; 
	
	QOS_PRIORITY qosPriority;
	::memset(&qosPriority, 0, sizeof(qosPriority));
	qosPriority.ObjectHdr.ObjectType   = QOS_OBJECT_PRIORITY;
	qosPriority.ObjectHdr.ObjectLength = sizeof(qosPriority);
	qosPriority.SendPriority = 184;

	Qos.ProviderSpecific.len = sizeof(qosPriority);
	Qos.ProviderSpecific.buf = reinterpret_cast<char*>(&qosPriority);
*/
	QOS_TRAFFIC_CLASS qosTraffic;
	::memset(&qosTraffic, 0, sizeof(qosTraffic));
	qosTraffic.ObjectHdr.ObjectType   = QOS_OBJECT_TRAFFIC_CLASS;
	qosTraffic.ObjectHdr.ObjectLength = sizeof(qosTraffic);
	qosTraffic.TrafficClass = 1;

	Qos.ProviderSpecific.len = sizeof(qosTraffic);
	Qos.ProviderSpecific.buf = reinterpret_cast<char*>(&qosTraffic);

	DWORD dwBytesRet;
	int nRet = ::WSAIoctl((RT_SOCKET)aSocket, SIO_SET_QOS, &Qos, sizeof(QOS), NULL, 0, &dwBytesRet, NULL, NULL);
	if (nRet == SOCKET_ERROR) {
		RT_ERROR_TRACE("CRtTransportBase::SetQos2Socket, WSAIoctl() failed!"
			" err=" << WSAGetLastError());
		return RT_ERROR_FAILURE;
	}
	else
		return RT_OK;

#else
	return RT_ERROR_NOT_IMPLEMENTED;
#endif // RT_WIN32
}

#endif // RT_SUPPORT_QOS
